/*
 Xern Entity Relocation Network
 Copyright (C) 2011 Ronie Salgado <roniesalg@gmail.com>

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
*/

#include "ShadowListener.hpp"
#include <OgreGpuProgramManager.h>

namespace XernOgre
{
	ShadowListener::ShadowListener(Ogre::SceneManager *sm, Ogre::ShadowCameraSetupPtr s)
	{
		sceneManager = sm;
		setup = s;
		pssm = new Ogre::PSSMShadowCameraSetup();
		lastCamera = NULL;
	}

	ShadowListener::~ShadowListener()
	{
		delete pssm;
	}

	void ShadowListener::shadowTexturesUpdated(size_t numberOfShadowTextures)
	{
	}

	void ShadowListener::shadowTextureCasterPreViewProj(Ogre::Light* light,Ogre::Camera* camera, size_t iteration)
	{
		// Only perform pssm in directional lights.
		if(light->getType() != Ogre::Light::LT_DIRECTIONAL)
			return;

		if(lastCamera != currentCamera)
		{
			const float lambda = 0.5f;
			pssm->calculateSplitPoints(3, currentCamera->getNearClipDistance(), sceneManager->getShadowFarDistance(), lambda);
			pssm->setSplitPadding(currentCamera->getNearClipDistance());
			pssm->setOptimalAdjustFactor(0, 2);
			pssm->setOptimalAdjustFactor(1, 1);
			pssm->setOptimalAdjustFactor(2, 0.5);
			lastCamera = currentCamera;

			// Update the split positions.
			Ogre::GpuSharedParametersPtr params =
					Ogre::GpuProgramManager::getSingleton().getSharedParameters("PssmParams");
			if(params != Ogre::GpuSharedParametersPtr ())
			{
				const Ogre::PSSMShadowCameraSetup::SplitPointList &splits = pssm->getSplitPoints();
				Ogre::Vector4 splitVec(splits[1], splits[2], splits[3], 0.0);

				//Notify the shaders about the splitting.
				params->setNamedConstant("ShadowSplits", splitVec);
			}
		}

		pssm->getShadowCamera(sceneManager, currentCamera, NULL, light, camera, iteration);
	}

	void ShadowListener::shadowTextureReceiverPreViewProj(Ogre::Light* light,Ogre::Frustum* frustum)
	{
	}

	struct lightsLess
	{
	    bool operator()(const Ogre::Light* l1, const Ogre::Light* l2) const
	    {
	        if (l1 == l2)
	            return false;
	        return l1->tempSquareDist < l2->tempSquareDist;
	    }
	};

	bool ShadowListener::sortLightsAffectingFrustum(Ogre::LightList& lightList)
	{
	    std::stable_sort(
	        lightList.begin(), lightList.end(),
	        lightsLess());

		return true;
	}

	void ShadowListener::SetCamera(Ogre::Camera *camera)
	{
		currentCamera = camera;
	}
} // namespace XernOgre
